import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:flutter/cupertino.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart';
import 'package:share_fit/app/controllers/base/base_sports_controller.dart';
import 'package:share_fit/app/routes/sports_routes.dart';
import 'package:share_fit/app/tools/date_tool.dart';
import 'package:share_fit/app/tools/unit_util.dart';
import 'package:share_fit/app/ute/uteped_const.dart';
import 'package:share_fit/app/controllers/home/exercise_records/model/exercise_type.dart';

import 'state.dart';

class MotionEndLogic extends BaseSportsController {
  final MotionEndState state = MotionEndState();

  GoogleMapController? googleMapController;

  @override
  String getRemotePath() {
    return UTEPedConst.pathSports;
  }

  @override
  void initState() {
    state.personAge = read("space_person_age", defaultValue: 0, global: true);
  }

  @override
  void onRemoteEvent(event, method) {
    // TODO: implement onRemoteEvent
  }

  @override
  void onReady() {
    List maps = state.workoutModel.detailList;
    if (maps.isNotEmpty) {
      state.altitudeMapList = maps.map((map) {
        map['y'] = map['elevation'];
        map['calendarTime'] = DateTool.toFullYMDHM(map['time']);
        state.altitudeList.add(map['elevation']);
        return map;
      }).toList();

      state.paceMapList = maps.map((map) {
        map['y'] = map['pace'] ~/ 60;
        map['calendarTime'] = DateTool.toFullYMDHM(map['time']);
        state.paceList.add(map['pace']);
        return map;
      }).toList();

      state.hrMapList = maps.map((map) {
        map['y'] = map['heartRate'];
        map['calendarTime'] = DateTool.toFullYMDHM(map['time']);
        state.heartRateList.add(map['heartRate']);
        return map;
      }).toList();

      state.cadenceMapList = maps.map((map) {
        map['y'] = map['stepFrequency'];
        map['calendarTime'] = DateTool.toFullYMDHM(map['time']);
        state.cadenceList.add(map['stepFrequency']);
        return map;
      }).toList();
    }

    super.onReady();
    update(['sports_end']);
  }

  int cadenceAverage() {
    if (state.cadenceList.isEmpty && state.workoutModel.frequency == null && state.workoutModel.frequency == 0) {
      return 0;
    }
    int? frequency = state.workoutModel.frequency;
    if (frequency == null || frequency == 0) {
      frequency = state.cadenceList.isEmpty ? 0 : (state.cadenceList.reduce((a, b) => a + b) / state.cadenceList.length).round();
    }
    return frequency;
  }

  int cadenceMax() {
    if (state.cadenceList.isEmpty) {
      return 0;
    }
    return state.cadenceList.reduce(max);
  }

  int cadenceMin() {
    if (state.cadenceList.isEmpty) {
      return 0;
    }
    return state.cadenceList.reduce(min);
  }

  int heartRateAverage() {
    int avgHeart = state.workoutModel.heart ?? 0;
    if (avgHeart > 0) {
      return avgHeart;
    }

    if (state.heartRateList.isNotEmpty) {
      return (state.heartRateList.reduce((a, b) => a + b) / state.heartRateList.length).floor();
    }
    return 0;
  }

  int heartRateMax() {
    if (state.heartRateList.isEmpty) {
      return 0;
    }
    return state.heartRateList.reduce(max);
  }

  int heartRateMin() {
    if (state.heartRateList.isEmpty) {
      return 0;
    }
    return state.heartRateList.reduce(min);
  }

  String paceAverage() {
    if (state.paceList.isEmpty && state.workoutModel.pace == null && state.workoutModel.pace == 0) {
      return '00\'00"';
    }
    int? pace = state.workoutModel.pace;
    if (pace == null || pace == 0) {
      pace = state.paceList.isEmpty ? 0 : (state.paceList.reduce((a, b) => a + b) / state.paceList.length).round();
    }

    if (UnitUtil.getLengthUnit() == 1) {
      pace = pace ~/ 0.62137;
    }
    return getPaceText(pace);
  }

  String paceMax() {
    if (state.paceList.isEmpty) {
      return '00\'00"';
    }
    return getPaceText(state.paceList.reduce(max));
  }

  String paceMin() {
    Iterable<int> list = state.paceList.where((element) => element > 0);
    if (list.isEmpty) {
      return '00\'00"';
    }
    return getPaceText(list.reduce(min));
  }

  String getPaceText(int pace) {
    var ms = '00\'00"';
    if (pace > 0) {
      int m = pace ~/ 60;
      int s = pace % 60;
      String sec = s < 10 ? '0$s' : '$s';
      ms = "$m'$sec\"";
    }
    return ms;
  }

  int altitudeAverage() {
    if (state.altitudeList.isEmpty) {
      return 0;
    }
    return (state.altitudeList.reduce((a, b) => a + b) / state.altitudeList.length).round();
  }

  int altitudeMax() {
    if (state.altitudeList.isEmpty) {
      return 0;
    }
    return state.altitudeList.reduce(max);
  }

  int altitudeMin() {
    if (state.altitudeList.isEmpty) {
      return 0;
    }
    return state.altitudeList.reduce(min);
  }

  hiddenMap() {
    return state.workoutModel.sportsType == null || !Type.isMotionTrail(state.workoutModel.sportsType!) || state.polyLine.points.isEmpty;
  }

  onMapCreated(GoogleMapController controller) {
    googleMapController = controller;
    if (state.polyLine.points.isNotEmpty) {
      List<LatLng> latLngList = state.polyLine.points;
      LatLngBounds bounds = boundsFromLatLngList(latLngList);
      controller.moveCamera(CameraUpdate.newLatLngBounds(bounds, 50));
    }
  }

  LatLngBounds boundsFromLatLngList(List<LatLng> latLngList) {
    double? x0, x1, y0, y1;
    for (LatLng latLng in latLngList) {
      if (x0 == null) {
        x0 = x1 = latLng.latitude;
        y0 = y1 = latLng.longitude;
      } else {
        if (latLng.latitude > x1!) x1 = latLng.latitude;
        if (latLng.latitude < x0) x0 = latLng.latitude;
        if (latLng.longitude > y1!) y1 = latLng.longitude;
        if (latLng.longitude < y0!) y0 = latLng.longitude;
      }
    }
    return LatLngBounds(northeast: LatLng(x1!, y1!), southwest: LatLng(x0!, y0!));
  }

  Future<File?> saveWidget(bool saveMap, GlobalKey key) async {
    File? dataFile = await saveWidgetAsImage(key);
    if (!saveMap) {
      return dataFile;
    }

    File? mapFile = await useMapSnapshot(googleMapController!);
    if (mapFile != null && mapFile.existsSync() && dataFile != null && dataFile.existsSync()) {
      ui.Image image1 = await getUIImage(mapFile.path);
      ui.Image image2 = await getUIImage(dataFile.path);

      if (image1.width > image2.width) {
        final directory = await getApplicationDocumentsDirectory();
        String tempPath = '${directory.path}/new_temp.png';
        final cmd = img.Command()
          ..decodeImageFile(mapFile.path)
          ..copyResize(width: (image2.width - 155.w).toInt())
          ..writeToFile(tempPath);
        await cmd.executeThread();

        image1 = await getUIImage(tempPath);
      }

      ui.PictureRecorder recorder = ui.PictureRecorder();
      ui.Canvas canvas = ui.Canvas(recorder);
      canvas.drawImage(image1, const Offset(0.0, 0.0), Paint());
      canvas.drawImage(
          image2,
          Offset(
            -80.w,
            image1.height.toDouble() - 1300.h,
          ),
          Paint());
      ui.Image combinedImage = await recorder.endRecording().toImage(image1.width, (image1.height + image2.height - 1300.h).toInt());
      var combined = await combinedImage.toByteData(format: ui.ImageByteFormat.png);
      Uint8List? pngBytes = combined?.buffer.asUint8List();

      final directory = await getApplicationDocumentsDirectory();
      File imgFile = File('${directory.path}/combined.png');
      File temp = await imgFile.writeAsBytes(pngBytes as List<int>);
      print('temp: ${temp.path}');

      mapFile.deleteSync();
      dataFile.deleteSync();

      return temp;
    }

    return dataFile;
  }

  Future<File?> saveWidgetAsImage(GlobalKey key) async {
    if (key.currentContext == null) {
      return null;
    }

    RenderRepaintBoundary boundary = key.currentContext!.findRenderObject() as RenderRepaintBoundary;
    final image = await boundary.toImage(pixelRatio: 2.0);
    final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
    Uint8List? pngBytes = byteData?.buffer.asUint8List();

    final directory = await getApplicationDocumentsDirectory();
    File imgFile = File('${directory.path}/temp_image_${DateTime.now().millisecondsSinceEpoch}.png');
    File temp = await imgFile.writeAsBytes(pngBytes as List<int>);
    return temp;
  }

  Future<Uint8List?> takeMapSnapshot(GoogleMapController controller) async {
    final Uint8List? snapshot = await controller.takeSnapshot();
    return snapshot;
  }

  Future<File?> useMapSnapshot(GoogleMapController controller) async {
    final Uint8List? snapshot = await takeMapSnapshot(controller);
    if (snapshot != null) {
      final directory = await getApplicationDocumentsDirectory();
      File imgFile = File('${directory.path}/temp_map_${DateTime.now().millisecondsSinceEpoch}.png');
      File temp = await imgFile.writeAsBytes(snapshot as List<int>);
      return temp;
    }
    return null;
  }

  getUIImage(String path) async {
    ui.Image uiImage;
    Completer<ui.Image> completer = Completer();
    Image image = Image.file(File.fromUri(Uri.parse(path)));
    image.image.resolve(ImageConfiguration()).addListener(ImageStreamListener((image, synchronousCall) {
      uiImage = image.image;
      completer.complete(uiImage);
    }));
    return completer.future;
  }

  _createEndMarker(LatLng latLng) async {
    BitmapDescriptor endIcon = await BitmapDescriptor.fromAssetImage(
      const ImageConfiguration(),
      Platform.isIOS ? "assets/images/sports/destination_ios.png" : "assets/images/sports/destination_android.png",
    );
    state.markers.last = Marker(
      markerId: const MarkerId('end'),
      position: latLng,
      icon: endIcon,
    );
  }

  _createStartMarker(LatLng latLng) async {
    BitmapDescriptor startIcon = await BitmapDescriptor.fromAssetImage(
      const ImageConfiguration(),
      Platform.isIOS ? "assets/images/sports/start_point_person_ios.png" : "assets/images/sports/start_point_person_android.png",
    );
    state.markers.first = Marker(
      markerId: const MarkerId('start'),
      position: latLng,
      icon: startIcon,
    );
  }

  Future<void> createMarkers() async {
    if (state.polyLine.points.isNotEmpty) {
      await _createStartMarker(state.polyLine.points.first);
      await _createEndMarker(state.polyLine.points.last);
      update(['motion_end_map']);
    }
  }

  goToSportsPage() {
    Future.delayed(Duration.zero, () {
      String backPage = state.fromPage == SportsRoutes.motionPage ? '/main' : state.fromPage!;
      Get.until((route) => Get.currentRoute == backPage);
    });
  }

  bool isOutdoorCycling() {
    return Type.rideBike.code == state.workoutModel.sportsType!;
  }

  bool isNoDistanceSports() {
    return !isDistanceSports();
  }

  bool isDistanceSports() {
    return Type.hasDistanceSports(state.workoutModel.sportsType!);
  }

  bool isNeedPaceChart() {
    return Type.isStepSports(state.workoutModel.sportsType!) || Type.isOutdoorCycling(state.workoutModel.sportsType!);
  }

  bool isNeedCadenceChart() {
    return isStepSports();
  }

  bool isStepSports() {
    return Type.isStepSports(state.workoutModel.sportsType!);
  }

  bool isNeedAltitudeChart() {
    return state.altitudeList.isNotEmpty && state.altitudeList.where((element) => element > 0).isNotEmpty;
  }

  String getLimitPercent() {
    return '0';
  }

  /// 获取单位转换之后的步幅
  getStride() {
    int stride = state.workoutModel.stride ?? 0;
    if (UnitUtil.getLengthUnit() == 1) {
      stride = (stride * 0.39370).floor();
    }
    return stride;
  }
}
